/*
 * gbdt_updater.cc
 *
 *  Created on: 2012-7-26
 *      Author: wanghaox
 */

#include "loss.h"
#include "gbdt_updater.h"

REGISTER_CLASS(GbdtUpdater);

bool GbdtUpdater::is_local_feature(int fid) {
	return _id == (fid % _count);
}

bool GbdtUpdater::parse_key_value(string &key, string &value) {
	// get token
	char* tok;
	if (not (tok = strtok(NULL, " \n"))) // line stored in state from previous call to strtok
		return false;

	// find colon
	string bit = tok;
	size_t colon_index = bit.find(":");

	// return empty value if colon is missing
	if (colon_index == bit.npos) {
		key = bit;
		value = string();
		return true;
	}

	// split string
	key = bit.substr(0, colon_index);
	value = bit.substr(colon_index + 1, bit.length() - colon_index - 1);

	return true;
}

bool GbdtUpdater::add_data(string &strline, int seq_no) {
	// setup for reading
//	int qid = -1;
	float label = -1.0f;
	string key, value;
	int feature_count = 0;

	// setup for tokenizing
	char* line = strdup(strline.c_str());
	char* tok = NULL;

	// extract label (first item) and check
	if (not (tok = strtok(line, " "))) {
		show("Error: malformed line in training file, missing label\n");
		return false;
	}
	label = atof(tok);
	_param._instance_lables[seq_no] = label;
	show("%d=%f", seq_no, label);

	// get qid, or ignore if not isrankingset
	string qidstr("qid");

	if (not parse_key_value(key, value))
		return true;
	if (value.empty()) {
		show("Error: invalid feature/value pair in training file\n");
		return false;
	}
	if (qidstr.compare(key)) {
		if (not parse_key_value(key, value))
			return true;
		if (value.empty()) {
			show("Error: invalid feature/value pair in training file\n");
			return false;
		}
	}

	// get feature values
	int feature_id = -1;
	float feature_value = -1.0f;
	do {
		// validate pair
		if (value.empty()) {
			show("Error: invalid feature/value pair in training file\n");
			return false;
		}
		feature_count++;

		// record feature
		feature_id = atoi(key.c_str());
		if (is_local_feature(feature_id)) {
			feature_value = (float) atof(value.c_str());

			if (_feature_map.find(feature_id) == _feature_map.end()) {
				_feature_map.insert(pair<int, GbdtFeature>(feature_id, GbdtFeature()));
				GbdtFeature &feature_set = _feature_map[feature_id];
				feature_set.init(_param._instance_num, feature_id);
			}

			GbdtFeature &feature_set = _feature_map[feature_id];
			feature_set.set(seq_no, feature_value);
//			show("%d %d %f", _feature_map.size(), seq_no, feature_value);
		}
	} while (parse_key_value(key, value));

	if (_param._global_feature_num < feature_count)
		_param._global_feature_num = feature_count;

	// clean up
	free(line);

	return true;
}

bool GbdtUpdater::iteration() {
	show("%d %d", _id, _feature_map.size());
	for (map<int, GbdtFeature>::iterator it = _feature_map.begin(); it != _feature_map.end(); ++it) {
		GbdtFeature &feature_set = it->second;
		_tree.find_local_split(_param, feature_set);
	}
	_tree.print_screen();
	return true;
}

bool GbdtUpdater::init() {
	// TODO copy instance lables
	int instance_num = atoi((*g_config)["global\\max_instance_num"].c_str());
	int tree_depth = atoi((*g_config)["global\\tree_depth"].c_str());

	show("%d", instance_num);

	_param.init(instance_num);
	_tree.init(tree_depth);

	_next_sync_step = GBDT_GATHER_BEGIN;
	return true;
}

bool GbdtUpdater::gather_from(Message *msg) {
	GbdtMessage *gbdt_msg = new GbdtMessage();

	switch (_next_sync_step) {
	case GBDT_GATHER_BEGIN:
		gbdt_msg->msg_type = GbdtMessage::GBDT_FEATURE_MSG;
		TreeNode &cur_node= _tree.
		_next_sync_step = GBDT_UPDATE_FEATURE;
		break;
	case GBDT_GATHER_NODE_DIST:
		gbdt_msg->msg_type = GbdtMessage::GBDT_NODE_DIST_MSG;
		_next_sync_step = GBDT_UPDATE_NODE_DIST;
		break;
	default:
		return false;
	}

	msg = gbdt_msg;
	return false;
}

bool GbdtUpdater::spread_to(Message *msg) {
	update_gradient();
//	GbdtMessage *gbdt_msg = (GbdtMessage*) msg;
//	switch (_next_sync_step) {
//	case GBDT_UPDATE_FEATURE:
//		_next_sync_step = GBDT_GATHER_NODE_DIST;
//		break;
//	case GBDT_UPDATE_NODE_DIST:
//		_next_sync_step = GBDT_GATHER_BEGIN;
//		break;
//	default:
//		return false;
//	}

	return false;
}

void GbdtUpdater::update_gradient() {
	compute_gradient(_param._instance_num, _param._instance_lables, _param._instance_prediction,
			_param._instance_residuals);
	for (int i = 0; i < _param._instance_num; i++) {
//		show("%f-%f=%f", _param._instance_lables[i], _param._instance_lables[i], _param._instance_residuals[i]);
	}
}

